<?php
namespace app\service;

use app\model\PhoneDn;
use support\Redis;
use think\facade\Db;

class DnService
{
    const AVAILABLE_KEY = 'dn_pool_set';
    const BIND_KEY = 'dn_bind_zset';
    const TTL = 600;

    // 初始化号池，传入数据库已用号码数组
    public static function initPool(array $usedDns = []): int
    {
        $allDns = range(1000, 9999);
        $available = array_diff($allDns, $usedDns);

        Redis::del(self::AVAILABLE_KEY);
        Redis::del(self::BIND_KEY);

        if (!empty($available)) {
            // 建议分批加以防参数过多
            $chunks = array_chunk($available, 1000);
            foreach ($chunks as $chunk) {
                Redis::sAdd(self::AVAILABLE_KEY, ...$chunk);
            }
        }

        return count($available);
    }

    // 分配分机号并写数据库
    public static function allocateDn(): int
    {
        // 限制最多绑定 5000 个
        if (Redis::zCard(self::BIND_KEY) >= 5000) {
            throw new \Exception('分机号已达最大绑定数限制（5000）');
        }

        // 从Redis分配分机号
        $dn = Redis::sPop(self::AVAILABLE_KEY);
        if ($dn === false) {
            throw new \Exception('没有可用的分机号了');
        }

        $expireAt = time() + self::TTL;
        Redis::zAdd(self::BIND_KEY, $expireAt, $dn);
        return (int)$dn;
    }

    // 释放分机号并删除数据库绑定
    public static function releaseDn(int $dn): void
    {
        Db::startTrans();
        try {
            PhoneDn::where('dn', $dn)->delete();

            Redis::zRem(self::BIND_KEY, $dn);
            Redis::sAdd(self::AVAILABLE_KEY, $dn);
            Db::commit();
        } catch (\Throwable $e) {
            Db::rollback();
            throw $e;
        }
    }

    // 清理过期绑定（数据库和Redis同步）
    public static function cleanupExpired(): void
    {
        $now = time();
        // 1. Redis过期回收
        $expiredDns = Redis::zRangeByScore(self::BIND_KEY, '-inf', $now);
        if (!empty($expiredDns)) {
            Redis::zRem(self::BIND_KEY, ...$expiredDns);
            Redis::sAdd(self::AVAILABLE_KEY, ...$expiredDns);
            // 同步删除数据库中对应绑定
            // PhoneDn::whereIn('dn', $expiredDns)->delete();
            foreach ($expiredDns as $dn) {
                //解绑dn
                (new YouYinService())->unbind($dn);
            }

        }

        // 2. 数据库超时同步 Redis（可选）
        $dbExpiredDns = PhoneDn::where('updated_at', '<', date('Y-m-d H:i:s', $now - self::TTL))->column('dn');
        foreach ($dbExpiredDns as $dn) {
            // 如果Redis没释放，强制释放
            if (!Redis::sIsMember(self::AVAILABLE_KEY, $dn)) {
                Redis::zRem(self::BIND_KEY, $dn);
                Redis::sAdd(self::AVAILABLE_KEY, $dn);
            }
        }
    }

    // 查询状态
    public static function status(): array
    {
        return [
            'available' => Redis::sCard(self::AVAILABLE_KEY),
            'binding' => Redis::zCard(self::BIND_KEY),
            'db_binding' => PhoneDn::count(),
        ];
    }
}
